/*
 * NetworkUtils.hpp
 *
 *  Created on: 10/lug/2009
 *      Author: Asteroth
 */

#ifndef __ENG_NETWORKUTILS_HPP__
#define __ENG_NETWORKUTILS_HPP__

#include <queue>
#include <boost/thread/mutex.hpp>

#include <estdlib/Defines.hpp>

namespace engine {
namespace net {

class EndPoint {
public:
	inline static boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> TCPv4(int port) {
		return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port);
	}

	inline static boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> TCPv6(int port) {
		return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), port);
	}

	inline static boost::asio::ip::basic_endpoint<boost::asio::ip::udp> UDPv4(int port) {
		return boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port);
	}

	inline static boost::asio::ip::basic_endpoint<boost::asio::ip::udp> UDPv6(int port) {
		return boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v6(), port);
	}
};

class NetworkMessage {
private:
	boost::asio::ip::tcp::socket* socket;
	char* data;
	int length;

	NetworkMessage() {}
	NetworkMessage(char* data, int length, boost::asio::ip::tcp::socket* socket = NULL) {
		this->data = data;
		this->length = length;
		this->socket = socket;
	}

public:
	~NetworkMessage() {
		delete[] data;
	}

	int getLength() const {return length;}
	char* getData() const {return data;}
	boost::asio::ip::tcp::socket* getSocket() {return socket;}

	void setData(char* data, int length) {this->data = data; this->length = length;}
	void setSocket(boost::asio::ip::tcp::socket* socket) {this->socket = socket;}

	void clear() {delete[] data; length = 0; socket = NULL;}

	estd::String getDataAsString() {return estd::String(data, length);}

	static NetworkMessage* create(char* data, int length, boost::asio::ip::tcp::socket* socket = NULL) {
		NetworkMessage* tmp = new NetworkMessage();
		tmp->socket = socket;
		if (length > 0 && data != NULL) {
			tmp->length = length;
			tmp->data = new char[length];
			eArrayCopy(char, tmp->data, data, length);
		} else {
			tmp->length = 0;
			tmp->data = NULL;
		}
		return tmp;
	}
	static NetworkMessage* create(const estd::String string, boost::asio::ip::tcp::socket* socket = NULL) {
		NetworkMessage* tmp = new NetworkMessage();
		tmp->socket = socket;
		if (string.length() > 0) {
			tmp->length = string.length();
			tmp->data = new char[tmp->length];
			eArrayCopy(char, tmp->data, string.string(), tmp->length);
		} else {
			tmp->length = 0;
			tmp->data = NULL;
		}
		return tmp;
	}
	static NetworkMessage* create(const NetworkMessage* message) {
		NetworkMessage* tmp = new NetworkMessage();
		tmp->socket = message->socket;
		if (message->length > 0 && message->data != NULL) {
			tmp->length = message->length;
			tmp->data = new char[message->length];
			eArrayCopy(char, tmp->data, message->data, message->length);
		} else {
			tmp->length = 0;
			tmp->data = NULL;
		}
		return tmp;
	}
};

class NetworkMessageQueue {
private:
	std::queue<NetworkMessage*> messages;
	boost::mutex qmutex;

public:
	NetworkMessageQueue() {}

	int count() const {return (int)messages.size();}

	NetworkMessage* next() {
		NetworkMessage* tmp = NULL;
		if (messages.size() > 0) {
			boost::mutex::scoped_lock lock(qmutex);
			tmp = messages.front();
			messages.pop();
		}
		return tmp;
	}

	void add(NetworkMessage* message) {
		boost::mutex::scoped_lock lock(qmutex);
		messages.push(message);
	}
};

}} //namespace

#endif
